//
//  MPDownloader.m
//  MissPaparazzi
//
//  Created by Alexey Aleshkov on 10.09.12.
//  Copyright (c) 2012 DzeTech. All rights reserved.
//


#import "MPDownloader.h"
#import "AFDownloadRequestOperation.h"


@interface MPDownloaderItem : NSObject
{
	MPDownloaderProgressBlock _progressBlock;
	MPDownloaderFailureBlock _failureBlock;
	MPDownloaderSuccessBlock _successBlock;
	NSString *_targetPath;
	NSString *_URLString;
}
@property (nonatomic, copy) NSString *URLString;
@property (nonatomic, copy) NSString *targetPath;
@property (nonatomic, copy) MPDownloaderSuccessBlock successBlock;
@property (nonatomic, copy) MPDownloaderFailureBlock failureBlock;
@property (nonatomic, copy) MPDownloaderProgressBlock progressBlock;
@end

@implementation MPDownloaderItem
@synthesize URLString = _URLString;
@synthesize targetPath = _targetPath;
@synthesize successBlock = _successBlock;
@synthesize failureBlock = _failureBlock;
@synthesize progressBlock = _progressBlock;
- (void)dealloc
{
	[_progressBlock release];
	[_failureBlock release];
	[_successBlock release];
	[_targetPath release];
	[_URLString release];
	[super dealloc];
}
@end


@interface MPDownloader ()
{
	NSRecursiveLock *_lock;
}
@property (nonatomic, retain) NSRecursiveLock *lock;
@end


@implementation MPDownloader

@synthesize lock = _lock;

#pragma mark -
#pragma mark Init / Dealloc

- (id)initWithBaseURL:(NSURL *)URL
{
	NSParameterAssert(URL != nil);
	
	self = [super init];
	if (!self) {
		return nil;
	}
	
    _lock = [[NSRecursiveLock alloc] init];
    _lock.name = @"com.dzetech.misspaparazzi.downloader";

	_processingRequests = [[NSMutableArray alloc] init];
	_pausedRequests = [[NSMutableArray alloc] init];
	
	_client = [[AFHTTPClient alloc] initWithBaseURL:URL];
	
	return self;
}

- (void)dealloc
{
	[_client release];
	[_pausedRequests release];
	[_processingRequests release];
	[_lock release];
	[super dealloc];
}

#pragma mark -
#pragma mark Private methods

- (void)enqueueItem:(MPDownloaderItem *)item shouldResume:(BOOL)shouldResume
{
	NSURL *URL = [NSURL URLWithString:item.URLString];
	NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:URL];
	AFDownloadRequestOperation *requestOperation = [[AFDownloadRequestOperation alloc] initWithRequest:URLRequest targetPath:item.targetPath shouldResume:shouldResume];
	void (^AFProgressBlock)(NSInteger, long long, long long, long long, long long) = ^(NSInteger bytesRead, long long totalBytesRead, long long totalBytesExpected, long long totalBytesReadForFile, long long totalBytesExpectedToReadForFile) {
		dispatch_async(dispatch_get_main_queue(), ^{
			if (item.progressBlock) {
				item.progressBlock(item.URLString, totalBytesReadForFile, totalBytesExpectedToReadForFile);
			}
		});
	};
	void (^AFSuccessBlock)(AFHTTPRequestOperation *, id) = ^(AFHTTPRequestOperation *operation, id responseObject) {
		if (item.successBlock) {
			item.successBlock(item.URLString, item.targetPath);
		}
	};
	void (^AFFailureBlock)(AFHTTPRequestOperation *, NSError *) = ^(AFHTTPRequestOperation *operation, NSError *error) {
		if (item.failureBlock) {
			item.failureBlock(item.URLString, item.targetPath, error);
		}
	};
	[requestOperation setProgressiveDownloadProgressBlock:AFProgressBlock];
	[requestOperation setCompletionBlockWithSuccess:AFSuccessBlock failure:AFFailureBlock];
	[_client enqueueHTTPRequestOperation:requestOperation];
	[requestOperation release];
}

#pragma mark -
#pragma mark Self methods

- (void)downloadFile:(NSString *)URLString toPath:(NSString *)path successBlock:(MPDownloaderSuccessBlock)successBlock failureBlock:(MPDownloaderFailureBlock)failureBlock progressBlock:(MPDownloaderProgressBlock)progressBlock
{
	[self.lock lock];
	
	NSOperationQueue *operationQueue = _client.operationQueue;
	for (NSInteger i = 0; i < operationQueue.operationCount; i++) {
		AFDownloadRequestOperation *requestOperation = [operationQueue.operations objectAtIndex:i];
		if ([requestOperation.request.URL.absoluteString isEqualToString:URLString] && [requestOperation.targetPath isEqualToString:path]) {
			return;
		}
	}
	
	MPDownloaderItem *item = [[MPDownloaderItem alloc] init];
	item.URLString = URLString;
	item.targetPath = path;
	item.successBlock = successBlock;
	item.failureBlock = failureBlock;
	item.progressBlock = progressBlock;

	[self enqueueItem:item shouldResume:NO];
	
	[_processingRequests addObject:item];
	[item release];
	
	[self.lock unlock];
}

- (void)pause:(NSString *)URLString
{
	[self.lock lock];
	
	for (AFDownloadRequestOperation *requestOperation in [_client.operationQueue operations]) {
		if ([requestOperation.request.URL.absoluteString isEqualToString:URLString]) {
			for (MPDownloaderItem *item in _processingRequests) {
				if ([item.URLString isEqualToString:URLString]) {
					[_pausedRequests addObject:item];
					[_processingRequests removeObject:item];
				}
			}
			[requestOperation cancel];
		}
	}
	
	[self.lock unlock];
}

- (void)pauseAll
{
	[self.lock lock];
	
	[_pausedRequests addObjectsFromArray:_processingRequests];
	[_processingRequests removeAllObjects];
	[_client.operationQueue cancelAllOperations];

	[self.lock unlock];
}

- (void)resume:(NSString *)URLString
{
	[self.lock lock];
	
	for (MPDownloaderItem *item in _pausedRequests) {
		if ([item.URLString isEqualToString:URLString]) {
			[self enqueueItem:item shouldResume:YES];
			
			[_processingRequests addObject:item];
			[_pausedRequests removeObject:item];
		}
	}
	
	[self.lock unlock];
}

- (void)resumeAll
{
	[self.lock lock];
	
	for (MPDownloaderItem *item in _pausedRequests) {
		[self enqueueItem:item shouldResume:YES];
		[_processingRequests addObject:item];
	}
	[_pausedRequests removeAllObjects];
	
	[self.lock unlock];
}

- (void)cancel:(NSString *)URLString
{
	[self.lock lock];
	
	for (AFDownloadRequestOperation *requestOperation in [_client.operationQueue operations]) {
		if ([requestOperation.request.URL.absoluteString isEqualToString:URLString]) {
			for (MPDownloaderItem *item in _processingRequests) {
				if ([item.URLString isEqualToString:URLString]) {
					[_processingRequests removeObject:item];
				}
			}
			[requestOperation cancel];
		}
	}
	
	[self.lock unlock];
}

- (void)cancelAll
{
	[self.lock lock];
	
	[_pausedRequests removeAllObjects];
	[_processingRequests removeAllObjects];
	[_client.operationQueue cancelAllOperations];
	
	[self.lock unlock];
}

@end
